#include "CBL_FolderFileSystem.h"
#include "CBL_Debug.h"

#ifdef CB_WINDOWS
#include <windows.h>
#else
#include <dirent.h>
#endif
#include <sys/stat.h>

CB_NAMESPACE_START

const int NativeSeekFlags[3] =
{
    SEEK_SET,
    SEEK_CUR,
    SEEK_END
};

// -------------------------------------------------------------------------------------------
//	CFolderFileSystem
// -------------------------------------------------------------------------------------------

CFolderFileSystem::CFolderFileSystem(const char* _pBaseFolder)
	: m_pBaseFolder(_pBaseFolder)
{
}

// -------------------------------------------------------------------------------------------

CFolderFileSystem::~CFolderFileSystem()
{
}

// -------------------------------------------------------------------------------------------

bool CFolderFileSystem::FileExists(const char* _pFileName) const
{
	std::string RealFileName;
    CreateFileName(_pFileName, RealFileName);
    
    FILE* pHandle = fopen(RealFileName.c_str(), "r");
    
    if (pHandle)
    {
    	fclose(pHandle);
        return true;  
    }
    
    return false;
}

// -------------------------------------------------------------------------------------------

int CFolderFileSystem::RemoveFile(const char* _pFileName) const
{
    std::string RealFileName;
    CreateFileName(_pFileName, RealFileName);

#ifdef CB_WINDOWS
	return _unlink(RealFileName.c_str());
#else
    return unlink(RealFileName.c_str());
#endif
}

// -------------------------------------------------------------------------------------------

CFileHandlePtr CFolderFileSystem::OpenFile(const char* _pFileName, uint8 _Flags) const
{
    std::string RealFileName;
    CreateFileName(_pFileName, RealFileName);
    
    IFileHandle* pFile = new CFileHandle(RealFileName, _Flags);
    return CFileHandlePtr(pFile);
}

// -------------------------------------------------------------------------------------------

bool CFolderFileSystem::FolderExists(const char* _pFolderName) const
{
	std::string RealFolderName;
    CreateFileName(_pFolderName, RealFolderName);
 
#ifdef CB_WINDOWS
	DWORD Attributes = GetFileAttributes(RealFolderName.c_str());
	if ((Attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
		return true;
#else
    DIR* pHandle = opendir(RealFolderName.c_str());
    
    if (pHandle)
    {
    	closedir(pHandle);
        return true;  
    }
#endif
    
    return false;   
}

// -------------------------------------------------------------------------------------------

int CFolderFileSystem::CreateFolder(const char* _pFolderName) const
{
	std::string RealFolderName;
    CreateFileName(_pFolderName, RealFolderName);

#ifdef CB_WINDOWS
	return CreateDirectory(RealFolderName.c_str(), NULL);
#else
    return mkdir(RealFolderName.c_str(), 0000755);
#endif
}

// -------------------------------------------------------------------------------------------

int CFolderFileSystem::RemoveFolder(const char* _pFolderName) const
{
	std::string RealFolderName;
    CreateFileName(_pFolderName, RealFolderName);

#ifdef CB_WINDOWS
	return RemoveDirectory(RealFolderName.c_str());
#else
	return rmdir(RealFolderName.c_str());
#endif
}

// -------------------------------------------------------------------------------------------

void CFolderFileSystem::GetFolderContents(const char* _pFolderName, std::vector<std::string>& _rOutList) const
{
	std::string RealFolderName;
    CreateFileName(_pFolderName, RealFolderName);

#ifdef CB_WINDOWS
	WIN32_FIND_DATA FileData;
	HANDLE Handle = FindFirstFile(RealFolderName.c_str(), &FileData);

	if (Handle != INVALID_HANDLE_VALUE)
	{
		do
		{
			_rOutList.push_back(std::string(FileData.cFileName));
		}
		while (FindNextFile(Handle, &FileData));
	}
#else
    DIR* pHandle = opendir(RealFolderName.c_str());
    
    if (pHandle)
    {
    	dirent* pFolderEntry;
        while (pFolderEntry = readdir(pHandle))
        {
            _rOutList.push_back(std::string(pFolderEntry->d_name));
        }
        
        closedir(pHandle);
    }
#endif
}

// -------------------------------------------------------------------------------------------

void CFolderFileSystem::CreateFileName(const char* _pFileName, std::string& _rOut) const
{
    _rOut += m_pBaseFolder;
    _rOut += CB_DIRECTORY_SEPARATOR;
    _rOut += _pFileName;
}

// -------------------------------------------------------------------------------------------
//	CFolderFileSystem::CFileHandle
// -------------------------------------------------------------------------------------------

CFolderFileSystem::CFileHandle::CFileHandle(std::string& _rFileName, uint8 _Flags)
{
	const char* pOpenFlags;

	switch (_Flags) 
    {
        case IFileSystem::OpenRead:
        	pOpenFlags = "r";
            break;
        
        case IFileSystem::OpenWrite:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite:
        	pOpenFlags = "r+";
        	break;
            
        case IFileSystem::OpenWrite | IFileSystem::OpenTruncate:
        case IFileSystem::OpenWrite | IFileSystem::OpenCreate | IFileSystem::OpenTruncate:
        	pOpenFlags = "w";
        	break;
            
        case IFileSystem::OpenTruncate:
        case IFileSystem::OpenTruncate | IFileSystem::OpenCreate:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite | IFileSystem::OpenTruncate:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite | IFileSystem::OpenTruncate | IFileSystem::OpenCreate:
        	pOpenFlags = "w+";
        	break;
            
        case IFileSystem::OpenWrite | IFileSystem::OpenCreate:
        case IFileSystem::OpenWrite | IFileSystem::OpenSeekEnd:
        case IFileSystem::OpenWrite | IFileSystem::OpenCreate | IFileSystem::OpenSeekEnd:
        	pOpenFlags = "a";
        	break;
            
        case IFileSystem::OpenCreate:
        case IFileSystem::OpenSeekEnd:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite | IFileSystem::OpenCreate:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite | IFileSystem::OpenSeekEnd:
        case IFileSystem::OpenRead | IFileSystem::OpenWrite | IFileSystem::OpenCreate | IFileSystem::OpenSeekEnd:
        	pOpenFlags = "a+";
        	break;
            
        default:
            Debug::Error("Invalid open flag combination: %d. \"%s\" is opened as ReadWriteCreate.", _Flags, _rFileName.c_str());
            pOpenFlags = "a+";
            break;
    }    
    
	m_pFile = fopen(_rFileName.c_str(), pOpenFlags);
    
    Debug::Assert(m_pFile != NULL, "Could not open \"%s\".", _rFileName.c_str());
}

// -------------------------------------------------------------------------------------------

CFolderFileSystem::CFileHandle::~CFileHandle()
{
	Close();
}

// -------------------------------------------------------------------------------------------

void CFolderFileSystem::CFileHandle::Close()
{
	if (m_pFile != NULL)
    {
        fclose(m_pFile);
        m_pFile = NULL;
    }
}

// -------------------------------------------------------------------------------------------

void CFolderFileSystem::CFileHandle::Seek(int32 _Position, ESeekPosition _From) const
{
	if (m_pFile != NULL)
    {
		fseek(m_pFile, (long)_Position, NativeSeekFlags[_From]);
    }
}

// -------------------------------------------------------------------------------------------

uint32 CFolderFileSystem::CFileHandle::GetFileSizeByte() const
{
	if (m_pFile != NULL)
    {
        long CurPos = ftell(m_pFile);
        
        fseek(m_pFile, 0, SEEK_END);
        
        long FileSize = ftell(m_pFile);
        
        fseek(m_pFile, CurPos, SEEK_SET);
        
        return (uint32)FileSize;
    }
    
    return 0;
}

// -------------------------------------------------------------------------------------------

uint32 CFolderFileSystem::CFileHandle::Read(char* _pBuffer, uint32 _Byte) const
{
    if (m_pFile != NULL)
    {
		return fread(_pBuffer, _Byte, 1, m_pFile);
    }
    
    return 0;
}

// -------------------------------------------------------------------------------------------

void CFolderFileSystem::CFileHandle::Write(const char* _pBuffer, uint32 _Byte) const
{
	if (m_pFile != NULL)
    {
		fwrite(_pBuffer, _Byte, 1, m_pFile);
    }
}

CB_NAMESPACE_END